package com.ringtheweb.serviceprovider.weather.dao.jpa;

import java.util.List;

import javax.persistence.EntityNotFoundException;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ringtheweb.persistence.dao.jpa.GenericJPADAO;
import com.ringtheweb.serviceprovider.weather.dao.ILocationDAO;
import com.ringtheweb.serviceprovider.weather.model.Location;
import com.ringtheweb.serviceprovider.weather.model.LocationType;

/**
 * Location DAO
 * @author ldewavri
 * 
 */
@Repository("locationDAO")
public class LocationDAO extends GenericJPADAO<Location, Long> implements ILocationDAO {
	
	// select count(*) as c , parent_location_id as pid , (select location_name from locations where id=pid)
	// from locations where parent_location_id is not null group by parent_location_id  order by c desc;
	private static Log LOG = LogFactory.getLog(LocationDAO.class);

	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Location findByNameAndParent(String name,
			Long parentId) {
		if (LOG.isDebugEnabled()) {
			LOG.debug(String.format("Find by name='%s' and parent id='%d'",name,parentId));
		}
		try {
			return (Location)getEntityManager().createNamedQuery("findLocationByNameAndParent")
			.setParameter("parentLocationId", parentId)
			.setParameter("locationName", name).getSingleResult();		
		}
		catch(NoResultException e) {
			LOG.debug(String.format("Location with name %s and parent id  %d not found",name,parentId));
		}
		catch(EntityNotFoundException e) {
			LOG.debug(String.format("Location with name %s and parent id  %d not found",name,parentId));
		}
		catch(NonUniqueResultException e) {
			LOG.debug(String.format("More that 1 location with name %s and parent id %d not found",name,parentId));
		}
		return null;

	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Location findByNameAndParentWithChildren(String name,
			Long parentId) {
		if (LOG.isDebugEnabled()) {
			LOG.debug(String.format("Find by name='%s' and id='%d'",name,parentId));
		}
		try {
			return (Location)getEntityManager().createNamedQuery("findLocationByNameAndParentWithChildren")
			.setParameter("parentLocationId", parentId)
			.setParameter("locationName", name).getSingleResult();		
		}
		catch(NoResultException e) {
			LOG.debug(String.format("Location with name %s and parent id  %d not found",name,parentId));
		}
		catch(EntityNotFoundException e) {
			LOG.debug(String.format("Location with name %s and parent id  %d not found",name,parentId));
		}
		catch(NonUniqueResultException e) {
			LOG.debug(String.format("More that 1 location with name %s and parent id %d not found",name,parentId));
		}
		return null;

	}

	@Override
	@SuppressWarnings("unchecked")
	@Transactional(propagation=Propagation.REQUIRED)
	public List<Location> findByIDWithChildren(Long locationId) {
		return getEntityManager().createNamedQuery("findLocationWithChildren")
					.setParameter("locationId", locationId).getResultList();
			
	}
	
	@Override
	@Transactional(propagation=Propagation.REQUIRED)
	public Location findByNameAndURLWithChildren(String name,String url) {
		try {
			return (Location)getEntityManager().createNamedQuery("findLocationByNameAndURLWithChildren")
			.setParameter("locationName", name)
			.setParameter("url", url)
			.getSingleResult();
		}
		catch(NoResultException e) {
			LOG.debug(String.format("Location with %s and %s not found",name,url));
		}
		catch(EntityNotFoundException e) {
			LOG.debug(String.format("Location with %s and %s not found",name,url));
		}
		catch(NonUniqueResultException e) {
			LOG.debug(String.format("More that 1 location with %s and %s not found",name,url));
		}
		
		return null;

			
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Location> findByTypeAndApprochingName(LocationType locationType,
			String name) {
		Criteria criteria = createCriteria();
		criteria.add(Restrictions.eq("locationType", locationType));
		criteria.add(Restrictions.like("name", name, MatchMode.START));
		criteria.setFetchMode("parentLocation", FetchMode.JOIN);
		return (List<Location>)criteria.list();
	}

	@Override
	public Location findByNameAndParentName(String name, String parentName,
			LocationType locationType) {
		
		try {
			return (Location)getEntityManager().createNamedQuery("findLocationByNameAndParentName")
			.setParameter("name", name)
			.setParameter("parentName", parentName)
			.setParameter("locationType", locationType)
			.getSingleResult();
		}
		catch(NoResultException e) {
			LOG.debug(String.format("Location with name %s and parentName %s and type %s not found",
					name,parentName,locationType.toString()));
		}
		catch(EntityNotFoundException e) {
			LOG.debug(String.format("Location with name %s and parentName %s and type %s not found",
					name,parentName,locationType.toString()));
		}
		catch(NonUniqueResultException e) {
			LOG.debug(String.format("Location with name %s and parentName %s and type %s not found",
					name,parentName,locationType.toString()));
		}
		
		return null;
	}



}
